﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Comp476Project
{
    class ParticleSystem
    {
        // Particle Subclass
        public class Particle
        {
            // Particle Attributes
            public Vector3 position, direction;
            public Matrix world;
            //public int type;
            public float size, rotX, rotZ, speed, gravitationalConstant;
            public double lifetime, age;
            public bool dead, gravity;
            public Color color;
            public static Random rand = new Random();
            // Particle Methods
            public Particle(/*int type, */float size, Vector3 position, Vector3 direction, Color color, double lifetime, float speed, float gravConst, bool grav)
            {
                //this.type = type;
                //if (this.type > 3) this.type = rand.Next(4);
                this.size = size;
                this.position = position;
                this.direction = direction;
                this.color = color;
                this.age = 0;
                this.lifetime = lifetime;
                this.rotX = 0;
                this.rotZ = 0;
                this.speed = speed;
                this.dead = false;
                this.gravitationalConstant = gravConst;
                this.gravity = grav;
                this.world = Matrix.CreateScale(size) * Matrix.CreateRotationX(rotX) * Matrix.CreateRotationZ(rotZ) * Matrix.CreateTranslation(position);
            }
            public void Reset(/*int type, */float size, Vector3 position, Vector3 direction, Color color, double lifetime, float speed, float gravConst, bool grav)
            {
                //this.type = type;
                //if (this.type > 3) this.type = rand.Next(4);
                this.size = size;
                this.position = position;
                this.direction = direction;
                this.color = color;
                this.age = 0;
                this.lifetime = lifetime;
                this.rotX = 0;
                this.rotZ = 0;
                this.speed = speed;
                this.dead = false;
                this.gravitationalConstant = gravConst;
                this.gravity = grav;
                this.world = Matrix.CreateScale(size) * Matrix.CreateRotationX(rotX) * Matrix.CreateRotationZ(rotZ) * Matrix.CreateTranslation(position);
            }
            public void Update(GameTime gameTime, bool rotate, bool becomeTransparent, float sizeMod)
            {
                age += gameTime.ElapsedGameTime.TotalMilliseconds;
                if (gravity)
                {
                    direction.Y -= gravitationalConstant;
                    direction.Normalize();
                }
                if (becomeTransparent && color.A > 0)
                {
                    color.A--; ;
                }
                position += speed * direction;
                if (rotate)
                {
                    rotX += 0.05f % MathHelper.ToRadians(360);
                    rotZ += 0.1f % MathHelper.ToRadians(360);
                }
                size += sizeMod;
                world = Matrix.CreateScale(size) * Matrix.CreateRotationX(rotX) * Matrix.CreateRotationZ(rotZ) * Matrix.CreateTranslation(position);
                if (age > lifetime)
                    dead = true;
            }
            public void Draw(Matrix view, Matrix projection, Primitives3D.GeometricPrimitive sphere)
            {
                sphere.Draw(world, view, projection, color);
            }
        }

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ PARTICLE SYSTEM @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        // System Attributes
        public Vector3 position, offset, direction, directionOffset, wind;
        public Matrix projection;
        public List<Particle> particles;
        public Primitives3D.GeometricPrimitive sphere;
        public int n, currentType;
        public double duration, timer;
        static Random rand = new Random();
        public bool gravity, alphaChange, rotate;
        public float gravitationalConstant, sizeMod, particleSize, sizeOffset, speed, speedOffset, respawn;
        public Color color;
        // System Methods
        public ParticleSystem(Primitives3D.GeometricPrimitive sphere, ref Matrix projection, int type, int N, double duration, bool gravity,
                                    float particleSize, float sizeOffset, float sizeMod, Color color, bool alphaChange, float speed, float speedOffset, bool rotate,
                                    Vector3 sourcePosition, Vector3 sourceOffset, Vector3 direction, Vector3 directionOffset, Vector3 wind, float respawn)
        {
            this.Initialize(sphere, ref projection, type, N, duration, gravity, particleSize, sizeOffset, sizeMod, color, alphaChange, speed,
                                speedOffset, rotate, sourcePosition, sourceOffset, direction, directionOffset, wind, respawn);
        }
        public void Initialize(Primitives3D.GeometricPrimitive sphere, ref Matrix projection, int type, int N, double duration, bool gravity,
                                    float particleSize, float sizeOffset, float sizeMod, Color color, bool alphaChange, float speed, float speedOffset, bool rotate,
                                    Vector3 sourcePosition, Vector3 sourceOffset, Vector3 direction, Vector3 directionOffset, Vector3 wind, float respawn)
        {
            // Initialize Attributes
            particles = new List<Particle>();
            this.projection = projection;
            this.n = N;
            this.duration = duration;
            this.timer = 0;
            this.gravity = gravity;
            this.gravitationalConstant = 0.02f;
            this.currentType = type;
            this.sphere = sphere;
            this.position = sourcePosition;
            this.offset = sourceOffset;
            this.direction = direction;
            this.directionOffset = directionOffset;
            this.wind = wind;
            this.particleSize = particleSize;
            this.sizeOffset = sizeOffset;
            this.sizeMod = sizeMod;
            this.color = color;
            this.alphaChange = alphaChange;
            this.speed = speed;
            this.speedOffset = speedOffset;
            this.rotate = rotate;
            this.respawn = respawn;
            // Generate Particles
            for (int i = 0; i != n; i++)
            {
                Vector3 dir = direction + new Vector3((float)rand.NextDouble() * (directionOffset.X * 2) - directionOffset.X,
                                                        (float)rand.NextDouble() * (directionOffset.Y * 2) - directionOffset.Y,
                                                        (float)rand.NextDouble() * (directionOffset.Z * 2) - directionOffset.Z);
                double lifetime = duration - (rand.NextDouble() * (duration / 2));
                dir.Normalize();
                float spd = speed + (float)rand.NextDouble() * speedOffset;
                Vector3 pos = position + new Vector3((float)rand.NextDouble() * offset.X, (float)rand.NextDouble() * offset.Y, (float)rand.NextDouble() * offset.Z);
                particles.Add(new Particle(/*this.currentType, */this.particleSize + (float)rand.NextDouble() * sizeOffset, pos, dir, this.color, lifetime, spd, this.gravitationalConstant, this.gravity));
            }
        }
        public void Reset()
        {
            // Generate Particles
            for (int i = 0; i != n; i++)
            {
                Vector3 dir = direction + new Vector3((float)rand.NextDouble() * (directionOffset.X * 2) - directionOffset.X,
                                                        (float)rand.NextDouble() * (directionOffset.Y * 2) - directionOffset.Y,
                                                        (float)rand.NextDouble() * (directionOffset.Z * 2) - directionOffset.Z);
                double lifetime = duration - (rand.NextDouble() * (duration / 2));
                dir.Normalize();
                float spd = speed + (float)rand.NextDouble() * speedOffset;
                Vector3 pos = position + new Vector3((float)rand.NextDouble() * offset.X, (float)rand.NextDouble() * offset.Y, (float)rand.NextDouble() * offset.Z);
                particles[i] = (new Particle(/*this.currentType, */this.particleSize + (float)rand.NextDouble() * sizeOffset, pos, dir, this.color, lifetime, spd, this.gravitationalConstant, this.gravity));
                particles[i].dead = false;
            }
        }
        public void Update(GameTime gameTime)
        {
            timer += gameTime.ElapsedGameTime.TotalMilliseconds;
            if (respawn !=0 && timer > respawn)
            {
                this.Reset();
                timer = 0;
            }
            foreach (Particle p in particles)
            {
                if (respawn == 0 && p.dead)
                {
                    Vector3 dir = direction + new Vector3((float)rand.NextDouble() * (directionOffset.X * 2) - directionOffset.X,
                                                        (float)rand.NextDouble() * (directionOffset.Y * 2) - directionOffset.Y,
                                                        (float)rand.NextDouble() * (directionOffset.Z * 2) - directionOffset.Z);
                    double lifetime = duration - (rand.NextDouble() * (duration / 2));
                    dir.Normalize();
                    float spd = speed + (float)rand.NextDouble() * speedOffset;
                    Vector3 pos = position + new Vector3((float)rand.NextDouble() * offset.X, (float)rand.NextDouble() * offset.Y, (float)rand.NextDouble() * offset.Z);
                    p.size = this.particleSize + (float)rand.NextDouble() * sizeOffset;
                    p.position = pos;
                    p.direction = dir;
                    p.color = this.color;
                    p.lifetime = lifetime;
                    p.speed = spd;
                    p.gravitationalConstant = this.gravitationalConstant;
                    p.gravity = this.gravity;
                    p.age = 0;
                    p.rotX = 0;
                    p.rotZ = 0;
                    p.dead = false;
                }
                if (!p.dead)
                    p.Update(gameTime, rotate, alphaChange, sizeMod);
            }
        }
        public void Draw(Matrix view)
        {
            foreach (Particle p in particles)
            {
                if (!p.dead)
                    p.Draw(view, projection, sphere);
            }
        }

    }
}


